// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: fengyang

#pragma once

#include <cstdint>

#include "coin2/exchange/base/feed/timer/timer_manager.h"

namespace coin2::exchange::base::feed::timer {
namespace impl {

// DeadlineTimer is not thread safe class, used for main thread only
// DeadlineTimer is used after FeedSubsystemManager initilization
// timestamp/duration is nanosecondsclass
/*

PsudoStrategy : public ::coin2::strategy::IStrategy {
  void Init(::coin2::app::Driver* driver) {
    timer_ = std::make_unique<DeadlineTimer>();
    // call onHeartbeatFeed every 3 seconds
    timer_->ExpiresEvery(3'000'000'000LL);
  }

  void onHeartbeatFeed(const FeedTimer& timer) override {
    std::cout << timer << std::endl;
  }

  std::unique_ptr<DeadlineTimer> timer_;
};

*/

class DeadlineTimer {
 public:
  explicit DeadlineTimer() {
    auto ptr = TimerManager::GetInstance();
    manager_ = ptr;
    timer_id_ = ptr->NewTimeId();
  }
  virtual ~DeadlineTimer() {
    auto ptr = manager_.lock();
    if (ptr) {
      ptr->Cancel(timer_id_);
    }
  }
  // expires at absulte nanosecond time
  uint64_t ExpiresAt(int64_t timestamp) {
    auto ptr = manager_.lock();
    if (ptr) {
      ptr->Cancel(timer_id_);
      auto callback = std::bind(&TimerManager::TriggerTimer, ptr.get());
      ptr->ExpiresAt(timer_id_, timestamp, callback);
      return timer_id_;
    }

    return 0;
  }
  // expires at relative nanosecond time to now
  uint64_t ExpiresFromNow(int64_t duration) {
    auto ptr = manager_.lock();
    if (ptr) {
      ptr->Cancel(timer_id_);
      auto callback = std::bind(&TimerManager::TriggerTimer, ptr.get());
      ptr->ExpiresFromNow(timer_id_, duration, callback);
      return timer_id_;
    }

    return 0;
  }
  // periodic expires on nanosecond duration,
  // first expries on relative nanosecond durtaion to now
  uint64_t ExpiresEvery(int64_t duration) {
    auto ptr = manager_.lock();
    if (ptr) {
      ptr->Cancel(timer_id_);
      auto callback = std::bind(&TimerManager::TriggerTimer, ptr.get());
      ptr->ExpiresEvery(timer_id_, duration, callback);
      return timer_id_;
    }

    return 0;
  }

  void Cancel() {
    auto ptr = manager_.lock();
    if (ptr) {
      ptr->Cancel(timer_id_);
    }
  }

  bool Running() {
    return Expires() > 0;
  }

  int64_t Expires() {
    auto ptr = manager_.lock();
    if (ptr) {
      return ptr->Expires(timer_id_);
    }

    return 0;
  }

  uint64_t Id() const {
    return timer_id_;
  }

 private:
  uint64_t timer_id_;
  std::weak_ptr<TimerManager> manager_;
};

}  // namespace impl

using impl::DeadlineTimer;


}  // namespace coin2::exchange::base::feed::timer
